package cn.ibizlab.util.adapter.security;

import cn.ibizlab.util.adapter.service.SysRuntimeModelService;
import cn.ibizlab.util.adapter.util.helper.CachedBeanCopier;
import cn.ibizlab.util.domain.IEntity;
import cn.ibizlab.util.filter.FilterBase;
import cn.ibizlab.util.filter.ISearchContext;
import cn.ibizlab.util.security.AuthenticationUser;
import cn.ibizlab.util.security.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import net.ibizsys.central.cloud.core.ServiceSystemRuntime;
import net.ibizsys.central.cloud.core.spring.rt.ServiceHub;
import net.ibizsys.central.dataentity.IDataEntityRuntime;
import net.ibizsys.central.dataentity.security.IDataEntityAccessManager;
import net.ibizsys.central.dataentity.service.IDEServiceAPIRuntime;
import net.ibizsys.central.service.ISysServiceAPIRuntime;
import net.ibizsys.central.util.EntityDTO;
import net.ibizsys.central.util.IEntityDTO;
import net.ibizsys.central.util.ISearchContextDTO;
import net.ibizsys.model.dataentity.defield.IPSDEField;
import net.ibizsys.model.dataentity.service.IPSDEServiceAPIMethod;
import net.ibizsys.model.dataentity.service.IPSDEServiceAPIRS;
import net.ibizsys.model.service.IPSSysServiceAPI;
import net.ibizsys.runtime.security.UserContext;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.core.Authentication;
import org.springframework.util.ObjectUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import static net.ibizsys.central.util.ISearchContext.*;

@Slf4j
public class DefaultAdapterMethodSecurityExpression extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private Object filterObject;

    private Object returnObject;

    private Object target;

    private SysRuntimeModelService sysRuntimeModelService;

    public boolean isEnablePermissionValid() {
        return SpringContextHolder.getApplicationContext().getEnvironment().getProperty("ibiz.enablePermissionValid", Boolean.class, false);
    }

    /**
     * Creates a new instance
     *
     * @param authentication the {@link Authentication} to use. Cannot be null.
     */
    public DefaultAdapterMethodSecurityExpression(Authentication authentication) {
        super(authentication);
    }

    @Override
    public boolean hasPermission(Object targetDomainObject, Object permission) {
        return hasPermission(null, null, targetDomainObject, permission);
    }

    public boolean hasPermission(String parent, Serializable parentKey, Object targetDomainObject, Object permission) {
        Authentication authentication = this.getAuthentication();
        if (!isEnablePermissionValid())
            return true;
        Object principal = authentication.getPrincipal();
        if (ObjectUtils.isEmpty(principal))
            return false;
        AuthenticationUser authenticationUser = null;
        if (principal instanceof AuthenticationUser) {
            authenticationUser = (AuthenticationUser) authentication.getPrincipal();
            if (authenticationUser.isSuperUser())
                return true;
        }

        String[] pairs = String.valueOf(permission).split("-|:");
        String entityTag = pairs.length >= 2 ? pairs[1] : "";
        String action = pairs.length >= 3 ? pairs[2] : "";

        if (targetDomainObject instanceof ArrayList && !((ArrayList<?>) targetDomainObject).isEmpty() && ((ArrayList<?>) targetDomainObject).get(0) instanceof IEntity) {
            for (IEntity entityBase : (List<IEntity>) targetDomainObject) {
                if (!test(parent, null, entityTag, action, entityBase))
                    return false;
            }
        } else if (targetDomainObject instanceof ISearchContext)
            return test(parent, parentKey, entityTag, action, targetDomainObject);
        else if (targetDomainObject instanceof IEntity)
            return test(parent, parentKey, entityTag, action, targetDomainObject);
        return true;
    }

    protected boolean test(String parent, Serializable parentKey, String entityTag, String action, Object obj) {
        ServiceHub serviceHub = SpringContextHolder.getBean(ServiceHub.class);
        ServiceSystemRuntime serviceSystemRuntime = serviceHub.getSystemRuntime(ServiceSystemRuntime.class, true);
        if (serviceSystemRuntime == null) {
            log.error("无法获取系统Runtime");
            return false;
        }
        List<IPSSysServiceAPI> iPSSysServiceAPIs = serviceSystemRuntime.getPSSystem().getAllPSSysServiceAPIs();
        if (iPSSysServiceAPIs != null) {
            for (IPSSysServiceAPI iPSSysServiceAPI : iPSSysServiceAPIs) {
                ISysServiceAPIRuntime iSysServiceAPIRuntime = serviceSystemRuntime.getSysServiceAPIRuntime(iPSSysServiceAPI.getCodeName(), true);
                if (iSysServiceAPIRuntime != null) {
                    IDataEntityRuntime iDataEntityRuntime = getSysRuntimeModelService().getDataEntityRuntime(entityTag, true);
                    IDEServiceAPIRuntime iDEServiceAPIRuntime = getSysRuntimeModelService().getDEServiceAPIRuntime(iSysServiceAPIRuntime, entityTag, true);
                    if(iDataEntityRuntime == null || iDEServiceAPIRuntime == null)
                        continue;

                    IPSDEServiceAPIMethod iPSDEServiceAPIMethod = getSysRuntimeModelService().getPSDEServiceAPIMethod(iDEServiceAPIRuntime, action, false);
                    String strAccessAction = iPSDEServiceAPIMethod.getDataAccessAction();
                    IDataEntityRuntime iParentDataEntityRuntime = !ObjectUtils.isEmpty(parent)? getSysRuntimeModelService().getDataEntityRuntime(parent, true) : null;
                    IDataEntityAccessManager iDataEntityAccessManager = iDataEntityRuntime.getDataEntityAccessManager();
                    try {
                        String strParentKey = parentKey == null ? null : String.valueOf(parentKey);
                        if (obj instanceof IEntity) {
                            IEntity entity = (IEntity) obj;
                            String idField = iDataEntityRuntime.getKeyPSDEField().getCodeName();
                            Object idValue = "";
                            if (!ObjectUtils.isEmpty(idField)) {
                                idValue = entity.get(idField);
                            }
                            IEntityDTO entityDTO = iDataEntityRuntime.createEntity();
                            CachedBeanCopier.copyEntity2Dto(entity,entityDTO);
                            //关系接口，填充parentId
                            if (iParentDataEntityRuntime != null) {
                                IPSDEServiceAPIRS iPSDEServiceAPIRS = iPSSysServiceAPI.getPSDEServiceAPIRSs().stream().filter(
                                        apirs -> apirs.getMajorPSDEServiceAPI().getPSDataEntity().getName().equals(iParentDataEntityRuntime.getName())
                                                && apirs.getMinorPSDEServiceAPI().getPSDataEntity().getName().equals(iDataEntityRuntime.getName())
                                ).findFirst().orElse(null);
                                IPSDEField parentIdPSDEField = iPSDEServiceAPIRS.getParentIdPSDEField();
                                if (parentIdPSDEField != null) {
                                    entityDTO.set(parentIdPSDEField.getCodeName(),parentKey);
                                }
                            }
                            return iDataEntityAccessManager.testDataAccessAction(UserContext.getCurrent(), iParentDataEntityRuntime, strParentKey, iPSDEServiceAPIMethod.isNeedResourceKey()? idValue : null, entityDTO, strAccessAction);
                        }
                        if (obj instanceof FilterBase) {
                            FilterBase filterBase = (FilterBase) obj;
                            ISearchContextDTO iSearchContextDTO = iDataEntityRuntime.createSearchContext(new HashMap() {{
                                put(PARAM_PAGE, filterBase.getPage());
                                put(PARAM_SIZE, filterBase.getSize());
                                put(PARAM_SORT, filterBase.getSort());
                            }});
                            boolean bok = iDataEntityAccessManager.testDataAccessAction(UserContext.getCurrent(), iParentDataEntityRuntime, strParentKey, iSearchContextDTO, strAccessAction);
                            if (bok) {
                                CachedBeanCopier.copyFilterDto2Entity(iSearchContextDTO, filterBase);
                            }
                            return bok;
                        }
                    } catch (Exception e) {
                        log.error("数据权限检查失败:" + e.getMessage(), e);
                        return false;
                    }
                }
            }
        }
        return false;
    }

    @Override
    public void setFilterObject(Object filterObject) {
        this.filterObject = filterObject;
    }

    @Override
    public Object getFilterObject() {
        return filterObject;
    }

    @Override
    public void setReturnObject(Object returnObject) {
        this.returnObject = returnObject;
    }

    @Override
    public Object getReturnObject() {
        return returnObject;
    }

    @Override
    public Object getThis() {
        return this.target;
    }

    void setThis(Object target) {
        this.target = target;
    }

    public SysRuntimeModelService getSysRuntimeModelService() {
        return sysRuntimeModelService;
    }

    public void setSysRuntimeModelService(SysRuntimeModelService sysRuntimeModelService) {
        this.sysRuntimeModelService = sysRuntimeModelService;
    }


}
